package com.gitee.jisong.concurrent;

import com.gitee.jisong.kit.Kit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.util.concurrent.*;

/**
 * 定时线程池
 * @author sunjisong 2019-07-24
 */
public class ThreadPoolBuilder {

    private Integer corePoolSize;

    private Integer maximumPoolSize;

    private Long keepAliveTime;

    private Boolean allowCoreThreadTimeOut;

    private BlockingQueue<Runnable> workQueue;

    private ThreadFactory threadFactory;

    private RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

    private TimeUnit timeUnit;

    private Thread thread = new Thread();

    private ThreadPoolBuilder(){}

    private class Thread{
        private String namePattern;
        private Boolean daemon;
        //线程优先级,为了避免滥用导致的饥饿.不设置
        private Integer priority;

        public Thread setNamePattern(String namePattern) {
            this.namePattern = namePattern;
            return this;
        }

        public Thread setDaemon(Boolean daemon) {
            this.daemon = daemon;
            return this;
        }
    }


    public ThreadPoolBuilder setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
        return this;
    }

    public ThreadPoolBuilder setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
        return this;
    }

    public ThreadPoolBuilder setKeepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
        return this;
    }

    public ThreadPoolBuilder setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        return this;
    }

    public ThreadPoolBuilder setThreadFactory(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        return this;
    }

    public ThreadPoolBuilder setHandler(RejectedExecutionHandler handler) {
        this.handler = handler;
        return this;
    }

    public ThreadPoolBuilder setThreadNamePattern(String threadNamePattern) {
        this.thread.setNamePattern(threadNamePattern);
        return this;
    }

    public ThreadPoolBuilder setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
        return this;
    }

    public Thread thread(){
        return this.thread;
    }

    /**
     * 防止无限制使用,限制最大为1000
     * @param workQueue
     * @return
     */
    public ThreadPoolBuilder setWorkQueue(BlockingQueue<Runnable> workQueue) {
        this.workQueue = workQueue;
        return this;
    }

    /**
     * 默认值
     */
    private void def(){
        if(Kit.def(corePoolSize,0) < 1){
            corePoolSize = Runtime.getRuntime().availableProcessors() * 6;
        }
        if(Kit.def(maximumPoolSize,0) < 1){
            maximumPoolSize = corePoolSize * 10;
        }

        if(Kit.def(keepAliveTime,0L) < 1){
            keepAliveTime = 0L;
        }

        if(null == threadFactory){
            threadFactory = new BasicThreadFactory.Builder()
                    .daemon(Kit.def(thread.daemon,Boolean.FALSE))
                    .namingPattern(thread.namePattern)
                    .build();
        }

        allowCoreThreadTimeOut = Kit.def(allowCoreThreadTimeOut,Boolean.FALSE);
        timeUnit = Kit.def(timeUnit,TimeUnit.NANOSECONDS);

        if(workQueue == null ){
            workQueue = new LinkedBlockingQueue<>(1000);
        }
    }

    public static ThreadPoolExecutor create(String threadNamePattern){
        return builder().setThreadNamePattern(threadNamePattern).build();
    }

    public static ThreadPoolBuilder builder(){
        return new ThreadPoolBuilder();
    }

    public ThreadPoolExecutor build(){
        check();
        def();
        return new ThreadPoolExecutor(
                this.corePoolSize,
                this.maximumPoolSize,
                this.keepAliveTime,
                this.timeUnit,
                this.workQueue,
                this.threadFactory,
                this.handler
        );
    }

    private void check() {
        if(StringUtils.isBlank(thread.namePattern)){
            throw new IllegalArgumentException("thread must have a meaningful name, namePattern can not be blank");
        }
        boolean isTooLarge = null != workQueue && workQueue.remainingCapacity() > 1000;
        if(isTooLarge){
            throw new IllegalArgumentException("workQueue too large capacity, the max is 1000, but found " + workQueue.remainingCapacity());
        }

    }

}
